#  Copyright (c) 2022. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
#  Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
#  Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
#  Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
#  Vestibulum commodo. Ut rhoncus gravida arcu.

# !/bin/env python
# -*- coding:utf-8
"""
@author:xingz
@file:maze_gui.py
@time:2022/02/22
"""
import tkinter
from tkinter import *
from zx_Maze.maze import *
from zx_search.generic_search import *


class MazeGui(Maze):
    def __init__(self, rows: int = 10, columns: int = 10, sparseness: float = 0.2, start: MazeLocation = MazeLocation(0, 0), goal: MazeLocation = MazeLocation(9, 9), grid_width=10):
        super().__init__(rows=rows, columns=columns, sparseness=sparseness, start=start, goal=goal)
        self.root_win = Tk()
        self.grid_width = grid_width
        self.root_win.title("Maze")
        self.root_win.geometry(f"{1000}x{1000}")
        self.canvas = Canvas(self.root_win, height=1000, width=1000, confine=False)
        self.canvas.pack(pady=10, padx=10)
        self.draw_maze()
        self.canvas.bind("<ButtonPress-1>", self.scroll_start)
        self.canvas.bind("<B1-Motion>", self.scroll_move)
        # linux scroll
        self.canvas.bind("<Button-4>", self.zoomerP)
        self.canvas.bind("<Button-5>", self.zoomerM)
        # windows scroll
        self.canvas.bind("<MouseWheel>", self.zoomer)

    def draw_maze(self):
        for row in range(len(self.grid)):
            for col in range(len(self.grid[row])):
                cell = self.grid[row][col]
                if cell == Cell.BLOCKED:
                    self.draw_blocked(row * self.grid_width - self.grid_width / 2, col * self.grid_width - self.grid_width / 2, (row + 1) * self.grid_width - self.grid_width / 2,
                                      (col + 1) * self.grid_width - self.grid_width / 2)
                elif cell == Cell.GOAL:
                    self.draw_goal(row * self.grid_width - self.grid_width / 2, col * self.grid_width - self.grid_width / 2, (row + 1) * self.grid_width - self.grid_width / 2,
                                   (col + 1) * self.grid_width - self.grid_width / 2)
                elif cell == Cell.START:
                    self.draw_start(row * self.grid_width - self.grid_width / 2, col * self.grid_width - self.grid_width / 2, (row + 1) * self.grid_width - self.grid_width / 2,
                                    (col + 1) * self.grid_width - self.grid_width / 2)

    def draw_blocked(self, minx, miny, maxx, maxy):
        self.canvas.create_rectangle(minx, miny, maxx, maxy, fill='gray')

    def draw_start(self, minx, miny, maxx, maxy):
        self.canvas.create_rectangle(minx, miny, maxx, maxy, fill='yellow')
        self.canvas.create_text((minx + maxx) // 2, (miny + miny) // 2, text="START")

    def draw_goal(self, minx, miny, maxx, maxy):
        self.canvas.create_rectangle(minx, miny, maxx, maxy, fill='green')

    def draw_path(self, path: list, **options):
        points = []
        for x, y in path:
            points.append(x)
            points.append(y)
        self.canvas.create_line(*points, **options)
        pass

    def scroll_start(self, event):
        self.canvas.scan_mark(event.x, event.y)

    def scroll_move(self, event):
        self.canvas.scan_dragto(event.x, event.y, gain=1)

    def tocanvasxy(self, event):
        return int(self.canvas.canvasx(event.x)), int(self.canvas.canvasx(event.x))

    # windows zoom
    def zoomer(self, event):
        x, y = self.tocanvasxy(event)
        if event.delta > 0:
            self.canvas.scale("all", x, y, 1.1, 1.1)
        elif event.delta < 0:
            self.canvas.scale("all", x, y, 0.9, 0.9)
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

    # linux zoom
    def zoomerP(self, event):
        x, y = self.tocanvasxy(event)
        self.canvas.scale("all", x, y, 1.1, 1.1)
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

    def zoomerM(self, event):
        x, y = self.tocanvasxy(event)
        self.canvas.scale("all", x, y, 0.9, 0.9)
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))


if __name__ == '__main__':
    mg = MazeGui(rows=100, columns=100, grid_width=10, goal=MazeLocation(99, 99))
    solution1 = bfs(mg.start, goal_test=mg.goal_test, successors=mg.successor)
    path1 = node_to_path(solution1)
    path1 = [[i.row * mg.grid_width, i.column * mg.grid_width] for i in path1]

    solution2 = dfs(mg.start, goal_test=mg.goal_test, successors=mg.successor)
    path2 = node_to_path(solution2)
    path2 = [[i.row * mg.grid_width, i.column * mg.grid_width] for i in path2]

    distance: Callable[[MazeLocation], float] = manhattan_distance(mg.goal)

    solution3 = astar(mg.start, goal_test=mg.goal_test, successor=mg.successor, heuristic=distance)
    path3 = node_to_path(solution3)
    path3 = [[i.row * mg.grid_width, i.column * mg.grid_width] for i in path3]

    option1 = {'fill': 'red', 'width': 2, 'capstyle': "round"}
    option2 = {'fill': 'blue', 'width': 2}
    option3 = {'fill': 'green', 'width': 2}
    mg.draw_path(path1, **option1)
    mg.draw_path(path2, **option2)
    mg.draw_path(path3, **option3)
    mg.canvas.mainloop()
